package com.shycloud.mido.common.pay.wxpay.utils;

import com.alibaba.fastjson.JSONObject;

import org.jdom.JDOMException;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.URL;
import java.util.*;

public class PayCommonUtil {

	private static final String UN_KNOWN = "unknown";

	public static String createNoncestr(int length) {
		String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		String res = "";
		for (int i = 0; i < length; i++) {
			Random rd = new Random();
			res += chars.indexOf(rd.nextInt(chars.length() - 1));
		}
		return res;
	}

	/**
	 * 生成随机字符串
	 */
	public static String createNoncestr() {
		String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		String res = "";
		for (int i = 0; i < 16; i++) {
			Random rd = new Random();
			res += chars.charAt(rd.nextInt(chars.length() - 1));
		}
		return res;
	}


	/**
	 * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
	 *
	 * @return boolean
	 */
	public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams) {
		StringBuffer sb = new StringBuffer();
		Set es = packageParams.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if (!"sign".equals(k) && null != v && !"".equals(v)) {
				sb.append(k + "=" + v + "&");
			}
		}

		sb.append("key=" + WxConfigUtil.API_KEY);

		//算出摘要
		String mysign = MD5Util.md5Encode(sb.toString(), characterEncoding).toLowerCase();
		String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();

		//System.out.println(tenpaySign + "    " + mysign);
		return tenpaySign.equals(mysign);
	}


	/**
	 * @param characterEncoding 编码格式
	 * @param parameters        请求参数
	 * @return
	 * @Description：sign签名
	 */
	public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v && !"".equals(v)
					&& !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + WxConfigUtil.API_KEY);
		String sign = MD5Util.md5Encode(sb.toString(), characterEncoding).toUpperCase();
		return sign;
	}

	/**
	 * @param characterEncoding 编码格式
	 * @param parameters        请求参数
	 * @return
	 * @Description：sign签名
	 */
	public static String createSignH5(String characterEncoding, SortedMap<Object, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v && !"".equals(v)
					&& !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + WxConfigUtil.API_KEY_H5);
		String sign = MD5Util.md5Encode(sb.toString(), characterEncoding).toUpperCase();
		return sign;
	}

	/**
	 * @param parameters 请求参数
	 * @return
	 * @Description：将请求参数转换为xml格式的string
	 */
	public static String getRequestXml(SortedMap<Object, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k)) {
				sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
			} else {
				sb.append("<" + k + ">" + v + "</" + k + ">");
			}
		}
		sb.append("</xml>");
		return sb.toString();
	}

	/**
	 * @param returnCode 返回编码
	 * @param returnMsg  返回信息
	 * @return
	 * @Description：返回给微信的参数
	 */
	public static String setXML(String returnCode, String returnMsg) {
		return "<xml><return_code><![CDATA[" + returnCode
				+ "]]></return_code><return_msg><![CDATA[" + returnMsg
				+ "]]></return_msg></xml>";
	}


	/**
	 * 发送https请求
	 *
	 * @param requestUrl    请求地址
	 * @param requestMethod 请求方式（GET、POST）
	 * @param outputStr     提交的数据
	 * @return 返回微信服务器响应的信息
	 */
	public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {

		try {

			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = {new MyX509TrustManager()};
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());

			// 从上述SSLContext对象中得到SSLSocketFactory对象
			//SSLSocketFactory ssf = sslContext.getSocketFactory();
			URL url = new URL(requestUrl);
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			//conn.setSSLSocketFactory(ssf);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);

			// 设置请求方式（GET/POST）
			conn.setRequestMethod(requestMethod);
			conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");

			// 当 outputStr不为 null 时向输出流写数据
			if (null != outputStr) {
				OutputStream outputStream = conn.getOutputStream();
				// 注意编码格式
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}
			// 从输入流读取返回内容
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			// 释放资源
			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			conn.disconnect();
			return buffer.toString();
		} catch (ConnectException ce) {
//          log.error("连接超时：{}", ce);
		} catch (Exception e) {
//          log.error("https请求异常：{}", e);
		}
		return null;
	}

	/**
	 * 发送https请求
	 *
	 * @param requestUrl    请求地址
	 * @param requestMethod 请求方式（GET、POST）
	 * @return JSONObject(通过JSONObject.get ( key)的方式获取json对象的属性值)
	 */
	public static JSONObject httpsRequest(String requestUrl, String requestMethod) {
		JSONObject jsonObject = null;
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = {new MyX509TrustManager()};
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();
			URL url = new URL(requestUrl);
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			//conn.setSSLSocketFactory(ssf);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			conn.setConnectTimeout(3000);
			// 设置请求方式（GET/POST）
			conn.setRequestMethod(requestMethod);
			//conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
			// 当outputStr不为null时向输出流写数据
			// 从输入流读取返回内容
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			// 释放资源
			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			conn.disconnect();
			jsonObject = JSONObject.parseObject(buffer.toString());
		} catch (ConnectException ce) {
//                    log.error("连接超时：{}", ce);
		} catch (Exception e) {
			System.out.println(e);
//                    log.error("https请求异常：{}", e);
		}
		return jsonObject;
	}

	public static String urlEncodeUTF8(String source) {
		String result = source;
		try {
			result = java.net.URLEncoder.encode(source, "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return result;
	}

	//无参函数, 非侵入式获取客户端ip地址
	public static String getIPAddress() {
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
		return getIPAddress(request);
	}

	public static String getIPAddress(HttpServletRequest request) {
		String ip = null;

		//X-Forwarded-For：Squid 服务代理
		String ipAddresses = request.getHeader("X-Forwarded-For");

		if (ipAddresses == null || ipAddresses.length() == 0 || UN_KNOWN.equalsIgnoreCase(ipAddresses)) {
			//Proxy-Client-IP：apache 服务代理
			ipAddresses = request.getHeader("Proxy-Client-IP");
		}

		if (ipAddresses == null || ipAddresses.length() == 0 || UN_KNOWN.equalsIgnoreCase(ipAddresses)) {
			//WL-Proxy-Client-IP：weblogic 服务代理
			ipAddresses = request.getHeader("WL-Proxy-Client-IP");
		}

		if (ipAddresses == null || ipAddresses.length() == 0 || UN_KNOWN.equalsIgnoreCase(ipAddresses)) {
			//HTTP_CLIENT_IP：有些代理服务器
			ipAddresses = request.getHeader("HTTP_CLIENT_IP");
		}

		if (ipAddresses == null || ipAddresses.length() == 0 || UN_KNOWN.equalsIgnoreCase(ipAddresses)) {
			//X-Real-IP：nginx服务代理
			ipAddresses = request.getHeader("X-Real-IP");
		}

		//有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
		if (ipAddresses != null && ipAddresses.length() != 0) {
			ip = ipAddresses.split(",")[0];
		}

		//还是不能获取到，最后再通过request.getRemoteAddr();获取
		if (ip == null || ip.length() == 0 || UN_KNOWN.equalsIgnoreCase(ipAddresses)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	public static Map<String, String> wxRequest(String url, String requestMethod, String encrypt,
			SortedMap<Object, Object> parameters) {
		Map<String, String> resultMap = new HashMap<String, String>();
		String resultXML = httpsRequest(url, requestMethod, commonSignPackage(parameters, encrypt));
		try {
			resultMap = XMLUtil.doXMLParse(resultXML);
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return resultMap;
	}

	public static Map<String, String> wxH5Request(String url, String requestMethod, String encrypt,
			SortedMap<Object, Object> parameters) {
		Map<String, String> resultMap = new HashMap<String, String>();
		String resultXML = httpsRequest(url, requestMethod, commonSignPackageH5(parameters, encrypt));
		try {
			resultMap = XMLUtil.doXMLParse(resultXML);
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return resultMap;
	}

	//统一微信签名方式
	//使用SortedMap默认根据key值进行升序排序
	public static String commonSignPackage(SortedMap<Object, Object> parameters, String encrypt) {
		String sign = PayCommonUtil.commonSignCreate(parameters, "UTF-8", WxConfigUtil.API_KEY, encrypt);
		parameters.put("sign", sign);
		//parameters.put("sign_type", encrypt);
		//最后直接返回xml字符串
		return getRequestXml(parameters);
	}

	public static String commonSignPackageH5(SortedMap<Object, Object> parameters, String encrypt) {
		String sign = PayCommonUtil.commonSignCreate(parameters, "UTF-8", WxConfigUtil.API_KEY_H5, encrypt);
		parameters.put("sign", sign);
		//parameters.put("sign_type", encrypt);
		//最后直接返回xml字符串
		return getRequestXml(parameters);
	}

	//创建签名
	public static String commonSignCreate(SortedMap<Object, Object> parameters, String characterEncoding, String key, String encrypt) {
		StringBuffer sb = new StringBuffer();
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v && !"".equals(v)
					&& !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + key);
		String sign = "";
		//根据需求选择不同的加密方式
		if (encrypt.equals("MD5")) {
			sign = MD5Util.md5Encode(sb.toString(), characterEncoding);
		}
		if (encrypt.equals("HMAC-SHA256")) {
			sign = SHA256.sha256Encode(sb.toString(), characterEncoding, key);
		}
		if (encrypt.equals("RSA")) {
			//RSA暂没用到RSA加签
			sign = "";
		}
		return sign.toUpperCase();
	}

	/**
	 * 创建统一下单签名map
	 *
	 * @param request
	 * @return
	 */
	public static SortedMap<Object, Object> createUnifiedSign(String appId, String price, String body, String notifyUrl,
			String outTradeNo,
			HttpServletRequest request) {

		SortedMap<Object, Object> map = new TreeMap<>();
		int price100 = new BigDecimal(price).multiply(new BigDecimal(100)).intValue();
		if (price100 <= 0) {
			map.put("flag", "false");
			return map;
		}
		map.put("appid", appId);
		map.put("mch_id", WxConfigUtil.MCH_ID);
		map.put("nonce_str", PayCommonUtil.createNoncestr());
		map.put("attach", "");
		map.put("body", body);
		map.put("detail", "");
		map.put("notify_url", notifyUrl);
		map.put("openid", "");
		map.put("out_trade_no", outTradeNo);
		map.put("spbill_create_ip", PayCommonUtil.getIPAddress(request));
		map.put("total_fee", String.valueOf(price100));
		map.put("trade_type", "APP");
		//设置签名
		String sign = PayCommonUtil.createSign("UTF-8", map);
		map.put("sign", sign);
		map.put("sign_type", "");
		return map;
	}

	/**
	 * 创建代扣协议 请求参数
	 *
	 * @param request
	 * @return
	 */
	public static SortedMap<Object, Object> createContractorderSign(String appId, String price, String body, String notifyUrl,
			String outTradeNo,
			String contractCode,
			String contractNotifyUrl,
			String contractDisplayAccount,
			long requestSerial,
			HttpServletRequest request) {

		SortedMap<Object, Object> map = new TreeMap<>();
		int price100 = new BigDecimal(price).multiply(new BigDecimal(100)).intValue();
		if (price100 <= 0) {
			map.put("flag", "false");
			return map;
		}

		//应用ID
		map.put("appid", appId);
		//商户号
		map.put("mch_id", WxConfigUtil.MCH_ID);
		//签约商户号
		map.put("contract_mchid", WxConfigUtil.MCH_ID);
		//签约appid
		map.put("contract_appid", appId);
		//商户的订单号
		map.put("out_trade_no", outTradeNo);
		//随机字符串
		map.put("nonce_str", PayCommonUtil.createNoncestr());
		//商品描述
		map.put("body", body);
		//回调地址
		map.put("notify_url", notifyUrl);
		//总金额
		map.put("total_fee", String.valueOf(price100));
		//终端IP
		map.put("spbill_create_ip", PayCommonUtil.getIPAddress(request));
		//交易类型
		map.put("trade_type", "APP");
		//模板ID
		map.put("plan_id", 11);
		//签约的协议号：由商户生成，只能是数字、大小写字母的描述
		map.put("contract_code", contractCode);
		//请求的序列号
		map.put("request_serial", requestSerial);
		//用户账户展示名称 用于页面展示
		map.put("contract_display_account", contractDisplayAccount);
		//签约信息通知url
		map.put("contract_notify_url", contractNotifyUrl);
		//签名
		map.put("sign", PayCommonUtil.createSign("UTF-8", map));

		return map;
	}

	public static SortedMap<Object, Object> createMiniUnifiedSign(String appId, String price, String body, String openId,
			String notifyUrl,
			String outTradeNo, HttpServletRequest request) {
		SortedMap<Object, Object> map = new TreeMap<>();
		int price100 = new BigDecimal(price).multiply(new BigDecimal(100)).intValue();
		if (price100 <= 0) {
			map.put("flag", "false");
			return map;
		}
		map.put("appid", appId);
		map.put("mch_id", WxConfigUtil.MCH_ID);
		map.put("nonce_str", PayCommonUtil.createNoncestr());
		map.put("attach", "");
		map.put("body", body);
		map.put("detail", "");
		map.put("notify_url", notifyUrl);
		map.put("openid", openId);
		map.put("out_trade_no", outTradeNo);
		map.put("spbill_create_ip", PayCommonUtil.getIPAddress(request));
		map.put("total_fee", String.valueOf(price100));
		map.put("trade_type", "JSAPI");
		//设置签名
		String sign = PayCommonUtil.createSign("UTF-8", map);
		map.put("sign", sign);

		return map;
	}
}